28장. JOIN이 사라진 세계 — 분산 환경에서의 조회
우리는 계속 시스템을 분리해왔다.
서비스가 나뉘고, 데이터베이스도 함께 나뉘었다.
이 변화는 하나의 중요한 결과를 만든다.
더 이상 JOIN을 사용할 수 없다
모놀리스에서는 문제가 아니었다
모놀리스에서는 조회가 단순했다.
하나의 DB 안에 모든 데이터가 있었기 때문이다.
SELECT *
FROM order o
JOIN user u ON o.user_id = u.id
WHERE u.age > 20;
JOIN으로 필요한 데이터를 한 번에 가져오면 끝이었다.
마이크로서비스에서는 달라진다
서비스가 분리되면 데이터도 분리된다.
- Order DB
- User DB
- Payment DB
이 상태에서는 다른 서비스의 DB를 직접 조회할 수 없다.
즉, JOIN이 사라진다.
문제는 조회에서 발생한다
쓰기(Write)는 비교적 단순하다.
각 서비스가 자신의 데이터를 처리하면 된다.
하지만 조회(Query)는 다르다.
화면은 여러 서비스의 데이터를 동시에 요구한다.
- 주문 + 사용자
- 주문 + 결제
- 통계 + 상세
이 시점에서 조회는 쿼리가 아니라 설계 문제가 된다.
분산 시스템에서는 조회도 설계해야 한다
단순한 해결은 통하지 않는다
처음에는 이렇게 생각하기 쉽다.
Order 조회 → User API 호출 → 데이터 조합
하지만 이 방식은 문제가 생긴다.
- 호출 횟수 증가 (N+1)
- 응답 속도 저하
- 장애 전파
결국 규모가 커지면 버틸 수 없다.
해결 전략들
1️⃣ API Composition
여러 서비스를 호출해서 데이터를 조합한다.
장점:
- 구현이 단순
- 실시간 데이터
단점:
- 성능 문제
- 장애 전파
2️⃣ 데이터 복제 (Denormalization)
다른 서비스 데이터를 일부 복제한다.
예:
- user_name
- user_age
장점:
- 조회 빠름
- JOIN 불필요
단점:
- 데이터 정합성
- 동기화 필요
비정규화는 선택이 아니라 전략이다
3️⃣ 이벤트 기반 조회 모델
이벤트를 받아 조회 전용 데이터를 만든다.
User 이벤트 → Order 조회 데이터 갱신
장점:
- 복잡한 조회 가능
- 성능 좋음
단점:
- 지연 발생
- 이벤트 처리 필요
→ CQRS Read 모델과 연결된다
Write는 정규화, Read는 비정규화
4️⃣ 검색/인덱스 시스템
데이터를 검색 시스템에 저장한다.
예:
- Elasticsearch
장점:
- 강력한 필터링
- 빠른 조회
단점:
- 별도 인프라
- 동기화 필요
어떤 전략을 선택할까
정답은 하나가 아니다.
상황에 따라 선택한다.
- 단순 조회 → API Composition
- 빠른 조회 → 데이터 복제
- 복잡 조회 → CQRS
- 검색 중심 → 검색 시스템
중요한 오해들
- API 호출로 JOIN 대체 가능 → ❌
- 데이터 복제는 나쁜 설계 → ❌
- 조회는 항상 최신이어야 한다 → ❌
이 장의 핵심
- JOIN은 사라진다
- 조회는 설계 문제다
- API 방식은 한계가 있다
- 비정규화는 필수 전략이다
- CQRS는 조회 문제 해결 구조다
- 상황에 맞게 선택해야 한다